package org.hawk.gwt.ppc.scanner.utils;

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;

import junit.framework.TestCase;

import org.hawk.gwt.ppc.javacc.JavaParserTreeConstants;
import org.hawk.gwt.ppc.javacc.JavaScanner;
import org.hawk.gwt.ppc.javacc.ParseException;
import org.hawk.gwt.ppc.scanner.Processor;
import org.hawk.gwt.ppc.scanner.Token;
import org.hawk.gwt.ppc.scanner.TokenChain;
import org.hawk.gwt.ppc.utils.Name;

public class JavaClassDescriptorTestCase extends TestCase {
	private static final String MOCK1 = "org.hawk.gwt.ppc.scanner.mock.Mock1";
	private static final String MOCK2 = "org.hawk.gwt.ppc.scanner.mock.Mock2";
	private static final String MOCK3 = "org.hawk.gwt.ppc.scanner.mock.Mock3";
	
	public void testCreate() throws IOException, ParseException{
		JavaClassDescriptor clazz = JavaClassDescriptorUtils
				.getClassDescriptor(MOCK1);
		assertEquals(Arrays.asList(new JavaVariable[]{
				new JavaVariable("class","java.lang.Class",0,null),
				new JavaVariable("VAR","int",0,null),
				new JavaVariable("zzz","java.lang.String",0,null),
				new JavaVariable("clazz","java.lang.Class<?>",0,null),
				new JavaVariable("bitset","java.util.BitSet",0,null),
				new JavaVariable("inner","java.lang.String",0,null),
				new JavaVariable("str","java.lang.Class<?>",0,null)
				}),
			clazz.getScope(88).getVariables(88));
		assertEquals(Arrays.asList(new JavaVariable[]{
				new JavaVariable("class","java.lang.Class",0,null),
				new JavaVariable("VAR","int",0,null),
				new JavaVariable("zzz","java.lang.String",0,null),
				new JavaVariable("clazz","java.lang.Class<?>",0,null),
				new JavaVariable("bitset","java.util.BitSet",0,null),
				new JavaVariable("name","java.lang.String",0,null)
				}),
			clazz.getScope(145).getVariables(145));
	}
	
	public void testGetVariableType()
			throws IOException, ParseException{
		JavaClassDescriptor clazz = JavaClassDescriptorUtils
			.getClassDescriptor(MOCK1);
		assertEquals("java.lang.Class<?>",
			clazz.getScope(88).getVariable(88,"str").getType());
		assertEquals("java.lang.Class<?>",
			clazz.getScope(88).getVariable(88,"clazz").getType());
		assertEquals("java.util.BitSet",
				clazz.getScope(124).getVariable(124,"bitset").getType());
		assertEquals("int",
				clazz.getScope(124).getVariable(124,"zzz").getType());
	}
	
	@SuppressWarnings("unchecked")
	public void testGetMethods()
		throws IOException, ParseException{
		JavaClassDescriptor clazz = JavaClassDescriptorUtils
			.getClassDescriptor(MOCK1);
		assertEquals(Arrays.asList(new JavaMethod[]{
				new JavaMethod("doSomething",null,0,
						new LinkedList<JavaVariable>(),"void"),
				new JavaMethod("getStringClass",null,0,
						new LinkedList<JavaVariable>(),"java.lang.Class<String>"),
				new JavaMethod("getStringClass",null,0,
						Arrays.asList(new JavaVariable[]{
								new JavaVariable("", "java.lang.String", 0, null)
				}),"java.lang.Class<String>"),
				new JavaMethod("getStringClass",null,0,
						Arrays.asList(new JavaVariable[]{
								new JavaVariable("", "java.lang.String", 0, null),
								new JavaVariable("", "int", 0, null)
				}),"java.lang.Class<String>"),
				new JavaMethod("getClass",null,0,
						Collections.EMPTY_LIST,"java.lang.Class"),
				new JavaMethod("equals",null,0,
						Arrays.asList(new JavaVariable[]{
							new JavaVariable("", "java.lang.Object", 0, null)
				}),"boolean"),
				new JavaMethod("toString",null,0,
						Collections.EMPTY_LIST,"java.lang.String"),
				new JavaMethod("hashCode",null,0,
						Collections.EMPTY_LIST,"int"),
				new JavaMethod("clone",null,0,
						Collections.EMPTY_LIST,"java.lang.Object"),
				new JavaMethod("finalize",null,0,
						Collections.EMPTY_LIST,"void")
			}),
			clazz.getCompilationUnit().getMethods());
	}
	
	public void testGetMethodReturnType()
		throws IOException, ParseException{
		JavaClassDescriptor clazz = JavaClassDescriptorUtils
			.getClassDescriptor(MOCK1);
		assertEquals("java.lang.Class<String>", clazz
				.getCompilationUnit().getMethod("getStringClass",
						"java.lang.String").getReturnType());
	}
	
	public void testSimpleExpressions() throws IOException, ParseException{
		JavaScanner scanner = new JavaScanner();
		scanner.addProcessor(JavaParserTreeConstants.JJTCOMPILATIONUNIT, new Processor() {
			public void visitStart(TokenChain<? extends Token> iterator, int kind) {}
			
			public void visitEnd(TokenChain<? extends Token> iterator, int kind) {
				try {
					assertEquals(String.class.getName(), 
						JavaClassDescriptorUtils.evaluateReturnTypeOfExpression(
							iterator, MOCK2, 21, 35));
					assertEquals(Class.class.getName(), 
						JavaClassDescriptorUtils.evaluateReturnTypeOfExpression(
							iterator, MOCK2, 36, 41));
					assertEquals(byte[].class.getName(), 
						JavaClassDescriptorUtils.evaluateReturnTypeOfExpression(
							iterator, MOCK2, 42, 61));
					assertEquals(Method.class.getName(), 
						JavaClassDescriptorUtils.evaluateReturnTypeOfExpression(
							iterator, MOCK2, 62, 94));
					assertEquals(Class.class.getName(), 
						JavaClassDescriptorUtils.evaluateReturnTypeOfExpression(
							iterator, MOCK2, 95, 118));
					assertEquals(Class.class.getName(), 
							JavaClassDescriptorUtils.evaluateReturnTypeOfExpression(
								iterator, MOCK2, 119, 151));
				}
				catch(IOException ex){
					throw new RuntimeException(ex);
				}
				catch(ParseException ex){
					throw new RuntimeException(ex);
				}
			}
		});
		scanner.startScan(new FileInputStream("./src/"+Name.javaToFs(MOCK2)), null);
	}
	
	public void testAnonimousClass() throws IOException, ParseException{
		JavaClassDescriptor clazz = JavaClassDescriptorUtils
			.getClassDescriptor(MOCK3);
		assertFalse(clazz.getCompilationUnit().getChildren().isEmpty());
		JavaMethod m = (JavaMethod) clazz.getCompilationUnit().getChildren().get(0);
		assertNotNull(m);
		assertFalse(m.getChildren().isEmpty());
		assertEquals("void",m.getReturnType());
		JavaScope mScope = m.getChildren().get(0);
		assertNotNull(mScope);
		assertFalse(mScope.getChildren().isEmpty());
		JavaScope anonimousClass = mScope.getChildren().get(0);
		assertNotNull(anonimousClass);
		assertTrue(anonimousClass instanceof JavaTypeScope);
		assertEquals(1,anonimousClass.getChildren().size());
		assertTrue(anonimousClass.getChildren().get(0) instanceof JavaMethod);
		JavaTypeScope type = (JavaTypeScope)anonimousClass;
		assertEquals(null, type.getClassName());
		assertEquals(Object.class.getName(),type.getSuperClass().getClassName());
		assertNotNull(type.getImplementedInterfaces());
		assertEquals(1, type.getImplementedInterfaces().size());
		assertEquals("com.google.gwt.event.dom.client.ClickHandler",
				type.getImplementedInterfaces().get(0).getClassName());
	}
}
